package com.doge.walknovel.business.home.library

import androidx.lifecycle.MutableLiveData
import com.doge.walknovel.analysis.analysisEvent
import com.doge.walknovel.analysis.novel_add_to_library
import com.doge.walknovel.api.*
import com.doge.walknovel.api.model.NovelInfo
import com.doge.walknovel.core.account.AccountHelper.self
import com.doge.walknovel.core.base.LoadStatus
import com.doge.walknovel.core.base.adapters.add
import com.doge.walknovel.core.base.adapters.remove
import com.doge.walknovel.core.base.adapters.replace
import com.doge.walknovel.utils.fromJson
import com.doge.walknovel.utils.isMainThread
import com.doge.walknovel.utils.runOnIO
import com.google.gson.reflect.TypeToken

class LibraryRepository {
    val adding = MutableLiveData<LoadStatus>()
    val loading = MutableLiveData<LoadStatus>()
    val dataList = MutableLiveData<List<LibraryNovel>>()

    fun requestMyLib() {
        require(isMainThread())
        loading.value = LoadStatus.LOADING

        if (!self.isLogin) {
            loading.value = LoadStatus.SUCCESS
            dataList.value = listOf(LibraryNovel.ADD)
            return
        }

        runOnIO {
            if (dataList.value == null) {
                val disk: String? = LibraryCacheHelper.loadCache()
                val cache =
                    fromJson<List<NovelInfo>>(disk, object : TypeToken<List<NovelInfo>>() {}.type)
                val result = mutableListOf<LibraryNovel>()
                result.addAll(cache?.map { LibraryNovel.from(it) } ?: listOf())
                result.add(LibraryNovel.ADD)
                dataList.postValue(result)
            }
            api
                .novelMyLib()
                .map(ApiResultMapTransformer())
                .observeOnMain()
                .subscribe(object : ObserverAdapter<List<NovelInfo>>() {
                    override fun onNext(t: List<NovelInfo>) {
                        loading.value = LoadStatus.SUCCESS
                        val result = mutableListOf<LibraryNovel>()
                        result.addAll(t.map { LibraryNovel.from(it) })
                        result.add(LibraryNovel.ADD)
                        dataList.value = result
                        runOnIO { LibraryCacheHelper.saveCache(t) }
                    }

                    override fun onError(e: Throwable) {
                        super.onError(e)
                        loading.value = LoadStatus.FAILURE
                        if (dataList.value == null) {
                            dataList.value = listOf(LibraryNovel.ADD)
                        }
                    }
                })
        }
    }

    fun requestDeleteNovel(data: NovelInfo) {
        require(isMainThread())
        api.novelRemove(data.novelId)
            .compose(ApiResultComposeTransformer())
            .subscribeOnIO()
            .subscribe(object : ObserverAdapter<ApiResult<Any?>>() {
                override fun onError(e: Throwable) {
                    // silently
                }
            })
        val list = dataList.value ?: return
        dataList.value = list.remove(list.find { it.book?.novelId == data.novelId })
    }

    fun requestAddNovel(data: NovelInfo?) {
        if (data == null) return
        require(isMainThread())
        if (containsNovel(data.novelId)) return

        analysisEvent(novel_add_to_library)
        adding.value = LoadStatus.LOADING
        api.novelSave(data.novelId)
            .compose(ApiResultComposeTransformer())
            .subscribeOnIO()
            .observeOnMain()
            .subscribe(object : ObserverAdapter<ApiResult<Any?>>() {
                override fun onNext(t: ApiResult<Any?>) {
                    localAddNovel(data)
                    adding.value = LoadStatus.SUCCESS
                }

                override fun onError(e: Throwable) {
                    // silently
                    e.printStackTrace()
                    adding.value = LoadStatus.FAILURE
                }
            })
    }

    fun updateEditMode(editMode: Boolean) {
        val list = dataList.value ?: return
        dataList.value = list.replace {
            it.copy(edit = editMode)
        }
    }

    fun updateSwitchEditMode() {
        val list = dataList.value ?: return
        val isInEditMode = list.any { it.edit }
        updateEditMode(!isInEditMode)
    }

    fun localAddNovel(bookInfo: NovelInfo) {
        val list = dataList.value ?: return
        dataList.value = list.add(0, LibraryNovel.from(bookInfo))
    }

    fun containsNovel(novelId: Int): Boolean {
        val list = dataList.value ?: return false
        return list.any { it.book?.novelId == novelId }
    }

    companion object {
        private var INS: LibraryRepository? = null

        fun get(): LibraryRepository {
            return INS ?: synchronized(this) {
                INS ?: LibraryRepository().also { INS = it }
            }
        }
    }
}